#!/usr/bin/env python
# -*- coding: utf-8 -*-

from threading import Lock

from farado.logger import logger
from farado.item_mapper import ItemMapper

from farado.items.user import User
from farado.items.issue_kind import IssueKind
from farado.items.field_kind import FieldKind
from farado.items.workflow import Workflow
from farado.items.state import State
from farado.items.edge import Edge
from farado.items.project import Project
from farado.items.version import Version

from farado.general_manager_holder import meta_item_manager



class ItemManager(ItemMapper):
    class Container:
        def __init__(self):
            self._lock = Lock()
            self._data = {}

        def item(self, id):
            if not id:
                return None

            with self._lock:
                id = int(id)
                if id in self._data:
                    return self._data[id]

            return None

        def items(self, ids=None):
            with self._lock:
                if ids is None:
                    return self._data.values()

                result = []
                for id in ids:
                    id = int(id)
                    if id in self._data:
                        result.append(self._data[id])

                return result

        def item_ids(self):
            with self._lock:
                return self._data.keys()

        def add_item(self, item):
            with self._lock:
                self._data[item.id] = item

        def add_items(self, items):
            with self._lock:
                for item in items:
                    self._data[item.id] = item

        def remove_item(self, item_id):
            with self._lock:
                self._data.pop(item_id, None)


    def __init__(self):
        super().__init__()
        self._users_container = ItemManager.Container()
        self._issue_kinds_container = ItemManager.Container()
        self._field_kinds_container = ItemManager.Container()
        self._workflows_container = ItemManager.Container()
        self._states_container = ItemManager.Container()
        self._edges_container = ItemManager.Container()
        self._projects_container = ItemManager.Container()
        self._versions_container = ItemManager.Container()

    def read_permanent_items(self):
        self._users_container.add_items(meta_item_manager().items(User))
        self._issue_kinds_container.add_items(meta_item_manager().items(IssueKind))
        self._field_kinds_container.add_items(meta_item_manager().items(FieldKind))
        self._workflows_container.add_items(meta_item_manager().items(Workflow))
        self._states_container.add_items(meta_item_manager().items(State))
        self._edges_container.add_items(meta_item_manager().items(Edge))
        self._projects_container.add_items(meta_item_manager().items(Project))
        self._versions_container.add_items(meta_item_manager().items(Version))

    # User ---------------------------------------------------------------------

    def user(self, user_id):
        return self._users_container.item(user_id)

    def users(self):
        return self._users_container.items()

    # IssueKind ----------------------------------------------------------------

    def issue_kind(self, issue_kind_id):
        return self._issue_kinds_container.item(issue_kind_id)

    def issue_kinds(self):
        return self._issue_kinds_container.items()

    def issue_kinds_by_workflow_id(self, workflow_id):
        return self._issue_kinds_container.items(
            self.issue_kinds_ids_by_workflow_id(workflow_id)
        )

    # FieldKind ----------------------------------------------------------------

    def field_kind(self, field_kind_id):
        return self._field_kinds_container.item(field_kind_id)

    def field_kinds(self):
        return self._field_kinds_container.items()

    def field_kinds_by_issue_kind_id(self, issue_kind_id):
        return self._field_kinds_container.items(
            self.field_kinds_ids_by_issue_kind_id(issue_kind_id)
        )

    # Workflow -----------------------------------------------------------------

    def workflow(self, workflow_id):
        return self._workflows_container.item(workflow_id)

    def workflows(self):
        return self._workflows_container.items()

    # State --------------------------------------------------------------------

    def state(self, state_id):
        return self._states_container.item(state_id)

    def states(self):
        return self._states_container.items()

    def states_by_workflow_id(self, workflow_id):
        return self._states_container.items(
            self.states_ids_by_workflow_id(workflow_id)
        )

    def ordered_states_by_workflow_id(self, workflow_id):
        return sorted(
            self.states_by_workflow_id(workflow_id),
            key=lambda state: state.order if state.order else 0
        )

    def archive_states_by_workflow_id(self, workflow_id):
        states = self.ordered_states_by_workflow_id(workflow_id)
        return [state for state in states if state.is_archive]

    # Edge ---------------------------------------------------------------------

    def edge(self, edge_id):
        return self._edges_container.item(edge_id)

    def edges(self):
        return self._edges_container.items()

    def edges_by_workflow_id(self, workflow_id):
        return self._edges_container.items(
            self.edges_ids_by_workflow_id(workflow_id)
        )

    # Project ------------------------------------------------------------------

    def project(self, project_id):
        return self._projects_container.item(project_id)

    def projects(self):
        return self._projects_container.items()

    # Version ------------------------------------------------------------------

    def version(self, version_id):
        return self._versions_container.item(version_id)

    def versions(self):
        return self._versions_container.items()

    def versions_by_project_id(self, project_id):
        return self._versions_container.items(
            self.versions_ids_by_project_id(project_id)
        )

    def ordered_versions_by_project_id(
            self,
            project_id,
            is_release_date_sort=True,
            reverse=False
        ):
        return sorted(
            self.versions_by_project_id(project_id),
            key=lambda version:
                version.release_date if is_release_date_sort else version.start_date,
            reverse=reverse
        )

    # Save/remove --------------------------------------------------------------

    def save_item(self, item):
        is_new_item = bool(item.id == None)
        meta_item_manager().add_item(item)
        if is_new_item:
            item_type = type(item)
            if item_type == User:
                self._users_container.add_item(item)

            elif item_type == IssueKind:
                self._issue_kinds_container.add_item(item)
                for sub_item in item.field_kinds:
                    self._field_kinds_container.add_item(sub_item)

            elif item_type == FieldKind:
                self._field_kinds_container.add_item(item)

            elif item_type == Workflow:
                self._workflows_container.add_item(item)
                for sub_item in item.states:
                    self._states_container.add_item(sub_item)
                for sub_item in item.edges:
                    self._edges_container.add_item(sub_item)

            elif item_type == State:
                self._states_container.add_item(item)

            elif item_type == Edge:
                self._edges_container.add_item(item)

            elif item_type == Project:
                self._projects_container.add_item(item)
                for sub_item in item.versions:
                    self._versions_container.add_item(sub_item)

            elif item_type == Version:
                self._versions_container.add_item(item)

    def remove_item(self, item_type, item_id):
        item_id = int(item_id)
        if item_type == User:
            self._users_container.remove_item(item_id)

        elif item_type == IssueKind:
            self._issue_kinds_container.remove_item(item_id)
            for sub_item_id in self.field_kinds_ids_by_issue_kind_id(item_id):
                self._field_kinds_container.remove_item(sub_item_id)

        elif item_type == FieldKind:
            self._field_kinds_container.remove_item(item_id)

        elif item_type == Workflow:
            self._workflows_container.remove_item(item_id)
            for sub_item_id in self.states_by_workflow_id(item_id):
                self._states_container.remove_item(sub_item_id)
            for sub_item_id in self.edges_by_workflow_id(item_id):
                self._edges_container.remove_item(sub_item_id)

        elif item_type == State:
            self._states_container.remove_item(item_id)

        elif item_type == Edge:
            self._edges_container.remove_item(item_id)

        elif item_type == Project:
            self._projects_container.remove_item(item_id)
            for sub_item_id in self.versions_by_project_id(item_id):
                self._versions_container.remove_item(sub_item_id)

        elif item_type == Version:
            self._versions_container.remove_item(item_id)

        meta_item_manager().delete_item_by_id(item_type, item_id)
